using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Reflection;
using RideMe.AddIns.Interfaces;

namespace RideMe.AddIns
{
    /// <summary>
    /// This internal class aids with the discovery of add-ins that RIDE-ME can use
    /// </summary>
    internal class Discovery
    {
        #region Static Variables

        /// <summary>
        /// This keeps track of whether the binaries have already been loaded or not
        /// </summary>
        private static bool _binariesLoaded = false;

        #endregion

        #region Static Methods

        /// <summary>
        /// This is a once-only call to load all binaries within the app base, or the private bin path
        /// </summary>
        private static void LoadBinaries()
        {
            if (_binariesLoaded)
                return;

            if(!string.IsNullOrEmpty(AppDomain.CurrentDomain.SetupInformation.ApplicationBase))
                LoadBinaries(AppDomain.CurrentDomain.SetupInformation.ApplicationBase);
            if(!string.IsNullOrEmpty(AppDomain.CurrentDomain.SetupInformation.PrivateBinPath))
                LoadBinaries(AppDomain.CurrentDomain.SetupInformation.PrivateBinPath);

            _binariesLoaded = true;
        }

        /// <summary>
        /// This loops through all DLL files in the specified directory, loading assemblies not previously loaded
        /// </summary>
        /// <param name="directory"></param>
        private static void LoadBinaries(string directory)
        {
            //Loop through all DLL files...
            foreach (string file in Directory.GetFiles(directory, "*.dll", SearchOption.AllDirectories))
            {
                try
                {
                    bool alreadyLoaded = false;
                    //Load the target assembly in reflection-only context
                    Assembly reflectedAssembly = Assembly.ReflectionOnlyLoadFrom(file);
                    //Loop through all existing loaded assemblies on the current domain
                    foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
                    {
                        //If the assembly we are inspecting is already loaded, set the flag to true
                        if (assembly.GetName().Name == reflectedAssembly.GetName().Name)
                            alreadyLoaded = true;
                    }
                    //If we get here and the assembly isn't already loaded, lets do so now
                    if (!alreadyLoaded)
                        Assembly.LoadFrom(file);
                }
                catch
                {
                }
            }
        }

        /// <summary>
        /// This returns all add-ins found for use with RIDE-ME
        /// </summary>
        /// <returns></returns>
        public static Collection<IAddIn> FindAddIns()
        {
            //Ensure all binaries are loaded
            LoadBinaries();
            //Create our return collection of add-ins
            Collection<IAddIn> addins = new Collection<IAddIn>();
            //Loop through all loaded assemblies...
            foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                //Loop through all types on that assembly...
                foreach (Type type in assembly.GetTypes())
                {
                    try
                    {
                        //Ensure the type implements IAddIn or else ignore it
                        if(!typeof(IAddIn).IsAssignableFrom(type))
                            continue;
                        //Likewise, ignore abstract implementations, and the interface itself
                        if (type.IsAbstract || type.IsInterface)
                            continue;
                        //If we get here, we found a valid add-in, so lets add it to our collection
                        addins.Add((IAddIn)Activator.CreateInstance(type));
                    }
                    catch
                    {
                    }
                }
            }
            //Return our collection of add-ins
            return addins;
        }

        #endregion
    }
}
